package com.linln.admin.medic.service.impl;

import cn.hutool.json.JSONUtil;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.linln.admin.medic.domain.MedicineInfo;
import com.linln.admin.medic.repository.MedicineInfoRepository;
import com.linln.admin.medic.scrawlVo.MedicItem;
import com.linln.admin.medic.scrawlVo.MedicResponseData;
import com.linln.admin.medic.service.MedicineInfoService;
import com.linln.common.data.PageSort;
import com.linln.common.enums.StatusEnum;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Example;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.IOException;
import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @author wholve
 * @date 2024/03/25
 */
@Service
public class MedicineInfoServiceImpl implements MedicineInfoService {

    @Autowired
    private MedicineInfoRepository medicineInfoRepository;

    /**
     * 根据ID查询数据
     *
     * @param id 主键ID
     */
    @Override
    public MedicineInfo getById(Integer id) {
        return medicineInfoRepository.findById(id).orElse(null);
    }

    @Override
    public MedicineInfo getByOriginId(Integer originId) {
        MedicineInfo medicineInfo = new MedicineInfo();
        medicineInfo.setOriginId(originId);
        // 获取数据列表
        Example<MedicineInfo> example = Example.of(medicineInfo);
        Optional<MedicineInfo> optional = medicineInfoRepository.findOne(example);
        return optional.orElse(null);
    }

    /**
     * 获取分页列表数据
     *
     * @param example 查询实例
     * @return 返回分页数据
     */
    @Override
    public Page<MedicineInfo> getPageList(Example<MedicineInfo> example) {
        // 创建分页对象
        PageRequest page = PageSort.pageRequest();
        return medicineInfoRepository.findAll(example, page);
    }

    /**
     * 保存数据
     *
     * @param medicineInfo 实体对象
     */
    @Override
    public MedicineInfo save(MedicineInfo medicineInfo) {
        return medicineInfoRepository.save(medicineInfo);
    }

    /**
     * 状态(启用，冻结，删除)/批量状态处理
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean updateStatus(StatusEnum statusEnum, List<Integer> idList) {
        return medicineInfoRepository.updateStatus(statusEnum.getCode(), idList) > 0;
    }

    @Override
    public void crawlBySearch(String keyword, Integer pageNo) {
        String searchUrl = "https://server.dayi.org.cn/api/search2?pageNo=" + pageNo + "&pageSize=30&keyword=" + keyword + "&type=medical";
        try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
            HttpGet request = new HttpGet(searchUrl);
            HttpResponse response = httpClient.execute(request);
            HttpEntity entity = response.getEntity();
            String htmlContent = EntityUtils.toString(entity);
            MedicResponseData medicResponseData = convertEntity(htmlContent, MedicResponseData.class);
            if (Objects.nonNull(medicResponseData) && CollectionUtils.isNotEmpty(medicResponseData.getList())) {
                List<MedicItem> list = medicResponseData.getList();
                list.forEach(medicItem -> {
                    int id = medicItem.getId();
                    MedicineInfo medicineInfo = this.getByOriginId(id);
                    if (Objects.isNull(medicineInfo)) {
                        Map<String, String> dataMap = processData(id);
                        MedicineInfo bean = JSONUtil.toBean(JSONUtil.toJsonStr(dataMap), MedicineInfo.class);
                        bean.setCreateDate(new Date());
                        bean.setUpdateDate(new Date());
                        bean.setUpdateBy("wh");
                        bean.setCreateBy("wh");
                        this.save(bean);
                    }
                });
                if (medicResponseData.getTotalCount() > pageNo * 30 && pageNo < 10) {
                    crawlBySearch(keyword, pageNo + 1);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    @Override
    public void crawlByThread(Integer no) throws InterruptedException {
        // 创建一个固定大小的线程池
        int threads = Runtime.getRuntime().availableProcessors();
        ExecutorService executorService = Executors.newFixedThreadPool(threads);
        for (int i = no; i < 3100000; i++) {
            // 将任务提交给线程池
            int finalI = i;
            executorService.submit(() -> {
                // 这里是每个任务需要执行的代码
                Map<String, String> dataMap = processData(finalI);
                if (MapUtils.isNotEmpty(dataMap) && StringUtils.isNotEmpty(dataMap.get("originId")) && StringUtils.isNotEmpty(dataMap.get("comName"))) {
                    MedicineInfo medicineInfo = this.getByOriginId(Integer.parseInt(dataMap.get("originId")));
                    if (Objects.isNull(medicineInfo)) {
                        MedicineInfo bean = JSONUtil.toBean(JSONUtil.toJsonStr(dataMap), MedicineInfo.class);
                        bean.setCreateDate(new Date());
                        bean.setUpdateDate(new Date());
                        bean.setUpdateBy("wh");
                        bean.setCreateBy("wh");
                        this.save(bean);
                    }
                }
            });
        }
        // 启动有序关闭，之前提交的任务将被执行，但不会接受新任务
        executorService.shutdown();
        // 等待所有任务完成，或超时，或当前线程被中断，视情况而定
        executorService.awaitTermination(1, TimeUnit.MINUTES);
    }

    private Map<String, String> processData(Integer id) {
        String url = "https://www.dayi.org.cn/drug/" + id + ".html"; // 替换为实际的网址
        Map<String, String> dataMap = new LinkedHashMap<>();
        try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
            HttpGet request = new HttpGet(url);
            HttpResponse response = httpClient.execute(request);
            HttpEntity entity = response.getEntity();
            String htmlContent = EntityUtils.toString(entity);
            // 定义正则表达式匹配模式
            String patternString = "(id|auditDoctor|medicalType|medicalTypeText|comName|chinesePinyin|shopName|englishName|property|drugType|rawMaterials|ytime|standard|adverseReactions|taboo|notice|medicFormat|component|healthType|storageMethod|interactions|recipeType|price|indication|dosage|pharmacokinetics|routeAdministration|dosageForm|thumb|brand|total|title|keywords)\\s*=\\s*\"?([^\";]+)\"?;|description\\s*=\\s*\"?([^\";]+)\"?;";
            Pattern pattern = Pattern.compile(patternString, Pattern.MULTILINE | Pattern.DOTALL);
            // 进行匹配
            Matcher matcher = pattern.matcher(htmlContent);
            Map<String, String> kvMap = getKVMap(htmlContent);
            // 遍历所有匹配结果并打印出来
            while (matcher.find()) {

                String key = matcher.group(1) != null ? matcher.group(1) : "description";
                String value = matcher.group(2) != null ? matcher.group(2) : matcher.group(3);
                if ("id".equals(key)) {
                    dataMap.put("originId", value);
                } else if (Objects.nonNull(kvMap.get(value))) {
                    dataMap.put(key, kvMap.get(value));
                } else {
                    dataMap.put(key, value);
                }
            }
        } catch (Exception e) {
            return dataMap;
        }
        return dataMap;
    }

    private Map<String, String> getKVMap(String htmlContent) {
        // 匹配参数名列表和参数值列表的正则表达式
        String regex = "window\\.__NUXT__\\s*=\\s*\\(function\\((.*?)\\)\\s*\\{[\\s\\S]*?\\}\\((.*?)\\);";
        Pattern pattern = Pattern.compile(regex);
        Matcher matcher = pattern.matcher(htmlContent);
        Map<String, String> paramMap = new LinkedHashMap<>();
        if (matcher.find()) {
            // 使用逗号加上非贪婪匹配来分割参数名，避免括号内的逗号干扰
            String[] paramNames = matcher.group(1).split(",\\s*");
            // 分隔参数值时考虑到可能存在的函数和数组，因此使用'},'作为潜在的分隔符
            String[] paramValues = matcher.group(2).split(",");
            try {
                for (int i = 0; i < paramNames.length; i++) {
                    String key = paramNames[i].trim();
                    if ("_".equals(key)) {
                        key = "a" + key;
                    }
                    String value = paramValues[i].replaceAll("[{}\"\\[\\]]", "").trim(); // 清除可能的大括号和方括号
                    paramMap.put(key, value);
                }
            } catch (Exception e) {
                System.out.println(e.getMessage());
            }
        }
        return paramMap;
    }

    public <T> T convertEntity(String htmlContent, Class<T> clazz) {
        T result;
        try {
            ObjectMapper objectMapper = new ObjectMapper();
            result = objectMapper.readValue(htmlContent, clazz);
        } catch (IOException e) {
            return null;
        }
        return result;
    }
}